#include "RtspManager.h"
#include <QList>
#include <QDebug>
#include <QFile>
#include <QApplication>
#include <QRegularExpression>

using namespace RTSP;

void RtspManager::setRtspUrl(const QByteArray &url)
{
    m_rtspUrl = url;
    m_sendCseq = 1;
    nextMethod = OPTIONS;
    setupMode = NONE;

    curRaplyData.clear();

    m_sessionMsg.clear();

    m_sdp.clear();
    curTrack = nullptr;
}

QByteArray RtspManager::parseReplyAndGetCmd(const QByteArray &recvMsg)
{
    try
    {
        // (VLC)有时raply和sdp会分开发送, 所以如果只收到raply, 则先只分析raply, 等后续收到再解析sdp并返回cmd
        // m_sdp.isEmpty() 表示是否已经解析过sdp
        if (curRaplyData.ContentLength == 0 || !m_sdp.isEmpty()) // 即 !(curRaplyData.ContentLength != 0 && m_sdp.isEmpty())
        {
            auto raply = recvMsg.left(recvMsg.indexOf("\r\n\r\n")); // 取消息前半部分(以 "\r\n\r\n" 分割)
            parseReply(raply, curRaplyData);

            if (curRaplyData.ContentLength != 0 && raply.size() + 4 + curRaplyData.ContentLength != recvMsg.size())
            {
                qDebug() << "only raply, wait sdp";
                return QByteArray();
            }
        }

        if (curRaplyData.ContentLength != 0) // 解析Sdp
        {
            auto sdpBuf = recvMsg.right(curRaplyData.ContentLength).trimmed();
            curRaplyData.ContentLength = 0;
            m_sdp.parse(sdpBuf);

            // writeSdp(sdpBuf);
        }

        if (nextMethod == OVER_PLAY)
            return QByteArray();

        return getCmd(curRaplyData.ResponseStateCode);
    }
    catch (const QString &e)
    {
        qDebug() << "error in parseReplyAndGetCmd:" << e;
        return QByteArray();
    }
}

void RtspManager::writeSdp(const QByteArray &sdp)
{
    QFile file(QApplication::applicationDirPath() + "/rtspSdpTmp.sdp");
    if (file.open(QIODevice::WriteOnly))
    {
        file.write(sdp);
        file.close();
    }
    else
    {
        qWarning() << "open file error";
    }
}

void RtspManager::parseReply(const QByteArray &reply, RaplyData &raplyData)
{
    // 切成(以 '\n' 分割)链表
    auto msgList = reply.split('\n');

    // 解析头一行
    if (sscanf(msgList.first().data(), "RTSP/1.0 %d", &raplyData.ResponseStateCode) != 1)
        throw QString("parse RTSP/1.0 error; ") + msgList.first();

    msgList.pop_front();

    // 解析剩余部分
    for (const auto msg : msgList)
    {
        int separator_index = msg.indexOf(':');
        if (separator_index == -1)
            throw QString("parse body error : ") + msg;

        auto head = msg.left(separator_index);
        auto value = msg.mid(separator_index + 1).trimmed();

        if (head.compare("Session", Qt::CaseInsensitive) == 0)
        {
            int _index = value.indexOf(';');
            auto sessionID = value.left(_index == -1 ? value.size() : _index);
            // if (raplyData.SessionID.compare(sessionID) != 0)
            // throw QString("SessionID error : ") + sessionID;
            // raplyData.SessionID = sessionID;
            m_sessionMsg = "Session: " + sessionID + "\r\n";
        }
        else if (head.compare("Cseq", Qt::CaseInsensitive) == 0)
        {
            // CSeq = value.toInt();
        }
        else if (head.compare("Content-Type", Qt::CaseInsensitive) == 0)
        {
        }
        else if (head.compare("Content-Base", Qt::CaseInsensitive) == 0)
        {
            // ContentBase = value;
        }
        else if (head.compare("Content-Length", Qt::CaseInsensitive) == 0)
        {
            raplyData.ContentLength = value.toInt();
        }
        else if (head.compare("Cache-Control", Qt::CaseInsensitive) == 0)
        {
        }
        else if (head.compare("Server", Qt::CaseInsensitive) == 0)
        {
        }
        else if (head.compare("Public", Qt::CaseInsensitive) == 0)
        {
        }
        else if (head.compare("Date", Qt::CaseInsensitive) == 0)
        {
        }
        else if (head.compare("Transport", Qt::CaseInsensitive) == 0)
        {
            {
                QRegularExpression re("client_port=(\\d+)-(\\d+)");
                QRegularExpressionMatch match = re.match(value);
                if (match.hasMatch())
                {
                    port_1 = match.captured(1).toInt();
                    port_2 = match.captured(2).toInt();
                }
            }
            {
                QRegularExpression re2("server_port=(\\d+)-(\\d+)");
                QRegularExpressionMatch match2 = re2.match(value);
                if (match2.hasMatch())
                {
                    server_port_1 = match2.captured(1).toInt();
                    server_port_2 = match2.captured(2).toInt();
                }
            }
        }
        else
        {
            qDebug() << QString("parse error : ") + head + ": " + value;
        }
    }
}

QByteArray RtspManager::getCmd(int ResponseStateCode)
{
    if (200 == ResponseStateCode)
    {
        return getNextCmd(nextMethod);
    }
    else if (461 == ResponseStateCode)
    {
        if (nextMethod == Method::SETUP)
        {
            if (setupMode == TCP)
            {
                setupMode = UDP;
                return getCmdSETUP(curTrack->getControl(), getUdpTransport(port_1, port_2));
            }
            else
            {
                // getCmdSETUP(curTrack->getControl(), getTcpTransport(curTrack->getTrackId()));
                // nextMethod = Method::SETUP_TCP;
                throw QString("461 Unsupported Transport");
            }
        }
    }
    else if (401 == ResponseStateCode)
    {
        throw QString("401 Unauthorized");
    }
    else if (404 == ResponseStateCode)
    {
        if (nextMethod == Method::SETUP)
            throw QString("404 Not Found, maybe url error");
        else
            throw QString("404 Not Found");
    }
    return QByteArray();
}

QByteArray RtspManager::getNextCmd(Method &next)
{
    QByteArray cmd;
    switch (next)
    {
    case Method::OPTIONS:
        cmd = getCmdOPTIONS();
        next = Method::DESCRIBE;
        break;
    case Method::DESCRIBE:
        cmd = getCmdDESCRIBE();
        next = Method::SETUP;
        setupMode = TCP;
        break;
    case Method::SETUP:
        curTrack = m_sdp.popTrack();
        if (curTrack)
        {
            cmd = getCmdSETUP(curTrack->getControl(), getTcpTransport(curTrack->getControlId()));
            break;
        }
        else
        {
            next = Method::PLAY; // 此处不跳出, 而是直接向下至PLAY
        }
    case Method::PLAY:
        cmd = getCmdPLAY();
        next = Method::OVER_PLAY;
        break;
    case Method::OVER_PLAY:
        qDebug() << "recv PLAY paply";
        break;
    default:
        qDebug() << "next error";
        break;
    }
    return cmd;
}

QByteArray RtspManager::getTcpTransport(int trackId)
{ // unicast:单播，multicast:多播
    int interleaved1 = trackId * 2;
    int interleaved2 = interleaved1 + 1;
    sprintf(m_bufSnd, "Transport: RTP/AVP/TCP;unicast;interleaved=%d-%d", interleaved1, interleaved2);

    return QByteArray(m_bufSnd);
}

QByteArray RtspManager::getUdpTransport(uint16_t client_port_1, uint16_t client_port_2)
{
    sprintf(m_bufSnd, "Transport: RTP/AVP;unicast;client_port=%d-%d", client_port_1, client_port_2);
    return QByteArray(m_bufSnd);
}

QByteArray RtspManager::getCmdOPTIONS()
{
    sprintf(m_bufSnd, "OPTIONS %s RTSP/1.0\r\n"
                      "CSeq: %d\r\n"
                      "User-Agent: %s\r\n"
                      "\r\n",
            m_rtspUrl.data(),
            m_sendCseq++,
            m_userAgent.data());

    return QByteArray(m_bufSnd);
}

QByteArray RtspManager::getCmdDESCRIBE()
{
    sprintf(m_bufSnd, "DESCRIBE %s RTSP/1.0\r\n"
                      "Accept: application/sdp\r\n"
                      "CSeq: %d\r\n"
                      "User-Agent: %s\r\n"
                      "\r\n",
            m_rtspUrl.data(),
            m_sendCseq++,
            m_userAgent.data());
    return QByteArray(m_bufSnd);
}

QByteArray RtspManager::getCmdSETUP(const QByteArray &control, const QByteArray &transport)
{
    sprintf(m_bufSnd, "SETUP %s/%s RTSP/1.0\r\n"
                      "%s\r\n"
                      "CSeq: %d\r\n"
                      "User-Agent: %s\r\n"
                      "%s\r\n",
            m_rtspUrl.data(), control.data(),
            transport.data(),
            m_sendCseq++,
            m_userAgent.data(),
            m_sessionMsg.data());
    return QByteArray(m_bufSnd);
}

QByteArray RtspManager::getCmdPLAY(/*QByteArray range*/)
{
    sprintf(m_bufSnd, "PLAY %s RTSP/1.0\r\n"
                      "Range: npt=0.000-\r\n"
                      "CSeq: %d\r\n"
                      "User-Agent: %s\r\n"
                      "%s\r\n",
            m_rtspUrl.data(),
            m_sendCseq++,
            m_userAgent.data(),
            m_sessionMsg.data());
    return QByteArray(m_bufSnd);
}

QByteArray RtspManager::getCmdTEARDOWN()
{
    if (nextMethod == Method::OPTIONS || nextMethod == Method::DESCRIBE)
        return QByteArray();

    sprintf(m_bufSnd, "TEARDOWN %s RTSP/1.0\r\n"
                      "CSeq: %d\r\n"
                      "User-Agent: %s\r\n"
                      "%s\r\n",
            m_rtspUrl.data(),
            m_sendCseq++,
            m_userAgent.data(),
            m_sessionMsg.data());
    return QByteArray(m_bufSnd);
}

QString RTSP::getMethodName(RTSP::Method method)
{
    QString str;
    switch (method)
    {
    case Method::OPTIONS:
        str = "OPTIONS";
        break;
    case Method::DESCRIBE:
        str = "DESCRIBE";
        break;
    case Method::SETUP:
        str = "SETUP";
        break;
    case Method::PLAY:
        str = "PLAY";
        break;
    case Method::OVER_PLAY:
        str = "OVER_PLAY";
        break;
    case Method::TEARDOWN:
        str = "TEARDOWN";
        break;
    default:
        qDebug() << "mothod error";
        break;
    }
    return str;
}